#pragma once
#include "Online_manger.hpp"

typedef enum {LOGIN, UNLOGIN}ss_status;
#define TIME_TEMP 30000
#define TIME_LONG -1
class session{
public:
    session(int ssid)
    :_ssid(ssid)
    {
        DLOG("%d 的ssid被创建", _ssid);
    }
    ~session()
    {
        DLOG("%d 的ssid被销毁",_ssid);
    }
    void set_user(int uid){_uid = uid;} //添加用户id
    void set_status(ss_status sstatus){_uid = sstatus;} //添加用户状态
    int get_user(){return _uid;}  //获取用户id
    bool is_login(){return _status == ss_status::LOGIN;} //判断该session用户当前是否为登录状态
    void set_timer(const wsserver_t::timer_ptr& tp) {_tp = tp;} //添加定时器
    wsserver_t::timer_ptr& get_timer(){return _tp;} //返回定时器
    int get_ssid(){return _ssid;}; //返回session的编号
private:
int _ssid; //用作查找的标识符
int _uid; //session用户对应的id
ss_status _status; //标识当前用户的状态
wsserver_t::timer_ptr _tp; //session相关的定时器
};

using session_ptr = std::shared_ptr<session>;

class session_manager
{
public:
session_manager(wsserver_t* server)
:_server(server),_next_sid(1)
{
    DLOG("创建session对象的管理器");
}
~session_manager()
{
    DLOG("销毁掉session对象管理器");
}

//根据用户uid与状态创建一个session对象指针
session_ptr create_session(int uid, ss_status status)
{
    unique_lock<mutex> lock(_mutex);
    session_ptr temp(new session(_next_sid));
    _sessions.insert(make_pair(_next_sid, temp));
    temp->set_status(status);
    temp->set_user(uid);
    _next_sid++;
    return temp;
}
//根据传入的ssid返回一个session对象指针
session_ptr get_session_from_ssid(int ssid)
{
    auto it = _sessions.find(ssid);
    if(it == _sessions.end())
    {
        return nullptr;
    }
    return it->second;
}

//在session末尾添加一个session
void append(session_ptr ssp)
{
    unique_lock<mutex> lock(_mutex);
    _sessions.insert(make_pair(ssp->get_ssid(), ssp));
}

//销毁掉一个seesion对象
void remove_session(int ssid)
{
    unique_lock<mutex> lock(_mutex);
    _sessions.erase(ssid);
}

//添加websocketpp中的一个定时器
void set_session_expir_time(int ssid, int ms)
{
    //依赖websocketpp中的定时器来完成session生命周期的管理。
    //登录之后，创建session,session需要在指定的ms时间内无通信后删除。
    //但是进入游戏大厅、房间，这个session就要被设置成永久存在。
    //等到退出游戏大厅、房间session重新再被设置为临时，在规定时间内无通信后被删除。
    //1、在session永久存在的情况下被设置为永久存在
    session_ptr ssp = get_session_from_ssid(ssid);
    if(ssp == nullptr) return ;
    wsserver_t::timer_ptr tp = ssp->get_timer();
    if(tp.get() == nullptr && ms == TIME_LONG) // get方法表示将智能指针转换为普通指针
    {
        return ;
    }
    //2、在session永久存在的情况下，被设置为临时存在
    else if(tp.get() == nullptr && ms == TIME_TEMP)
    {
        //给websocketpp加上一个定时器任务，通过bind函数进行绑定。
        wsserver_t::timer_ptr tmp_tp = _server->set_timer(TIME_TEMP, bind(&session_manager::remove_session, this, ssid));
        ssp->set_timer(tmp_tp);
    }
    //3、在session临时存在情况下，被设置为永久存在
    else if(tp.get() != nullptr && ms == TIME_LONG)
    {
        wsserver_t::timer_ptr tmp_tp = ssp->get_timer(); //获取到当前session对象
        tmp_tp->cancel(); //结束掉定时器, 因为上面提前拷贝了一份session_ptr到ssp中，所以这里执行earse不会导致session_ptr直接消失。
        ssp->set_timer(wsserver_t::timer_ptr());  //将session类中的定时器设置为空
        _server->set_timer(0, bind(&session_manager::append,this, ssp)); //因为cancel不会立马执行回调函数，所以我们也得把添加session节点函数放至定时器中。防止数据添加效果消失。
    }
    //4、在session临时存在情况下，被设置为临时存在(重置刷新时间)
    else if(tp.get() != nullptr && ms == TIME_TEMP)
    {
        wsserver_t::timer_ptr tmp_tp = ssp->get_timer(); //获取到当前session对象
        tmp_tp->cancel(); //结束掉定时器, 因为上面提前拷贝了一份session_ptr到ssp中，所以这里执行earse不会导致session_ptr直接消失。
        _server->set_timer(0, bind(&session_manager::append,this, ssp)); //因为cancel不会立马执行回调函数，所以我们也得把添加session节点函数放至定时器中。防止数据添加效果消失。
        tmp_tp = _server->set_timer(TIME_TEMP, bind(&session_manager::remove_session, this, ssid));
        ssp->set_timer(tmp_tp);
    }
    if(ms == TIME_LONG)
    {
        DLOG("%d 号连接的事件被设置为永久", ssid);
    }else
    {
       DLOG("%d 号连接的事件被设置为临时", ssid); 
    }
}

private:
int _next_sid; //用于分配ssid时的计数器
std::mutex _mutex; 
std::unordered_map<int, session_ptr> _sessions; //用作管理session的结构
wsserver_t* _server; //用户对应的websocketpp的连接
};